package com.plat.api.common.base;

import cn.binarywang.wx.miniapp.api.WxMaService;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.plat.api.anonation.RequestLimit;
import com.plat.api.common.OrderType;
import com.plat.api.common.RespCode;
import com.plat.api.dao.api.*;
import com.plat.api.entity.api.*;
import com.plat.api.exception.PlatException;
import com.plat.api.util.FileUtils;
import com.plat.api.util.PropertiesUtil;
import com.plat.api.util.QiNiuUtil;
import com.plat.common.utils.file.FileUploadUtils;
import com.plat.framework.redis.RedisCache;
import com.plat.project.app.SystemParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import net.coobird.thumbnailator.Thumbnails;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * author ss
 * createTime 2020/5/8
 * package ${com.example.demo.controller}
 ***/
@RestController
@RequestMapping("/open")
@Slf4j
@Controller
public class HttpController {
    @Autowired
    private SystemParam systemParam;
    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private WxPayService wxService;
    @Autowired
    private PlatOrderDao platOrderDao;
    @Autowired
    private PlatOrderDetailDao platOrderDetailDao;
    @Autowired
    private PlatAuctionDao platAuctionDao;
    @Autowired
    private PlatAuctionOrderDao platAuctionOrderDao;
    @Autowired
    private PlatAuctionOrderRecordDao platAuctionOrderRecordDao;


    @Autowired
    private RedisCache redisCache;

    //报文体要求
//{
//    "head":{
//    "cmd":"phoneService.invoke",
//            "token":"token",
//            "service":"user-center",
//            "version":"1.0"
//},
//    "body":{
//    "name":"zahnsan",
//            "age":"18"
//}
//}
//响应报文
//
//    {
//          "code":"2000",
//            "success":"success",
//            "timestimp":"202001122",
//            "msg":"成功",
//            "cmd":"cmd",
//            "service":"user-center",
//            "body":{
//
//    }
//    }
    @RequestMapping("/api/upload")
    public void upload(HttpServletRequest req, HttpServletResponse rep) throws IOException {
        System.out.printf("sasaas");
        resp(rep, "  resp(rep, result, cmd);", "upload");
    }

    @RequestMapping("/api")
    @RequestLimit(count = 10, time = 1000)
    public void start(HttpServletRequest req, HttpServletResponse rep) throws IOException, WxErrorException {
        Object result = null;
        String cmd = null;
        if (req.getMethod().equals("POST")) {
            if (!req.getContentType().contains("multipart/")) {
                JSONObject reqBody = new JSONObject();
                StringBuffer sb = new StringBuffer();
                try {
                    sb.append(req.getReader().readLine());
                    reqBody = JSONObject.parseObject(sb.toString().trim());
                    checkReqBody(reqBody);
                    cmd = reqBody.getJSONObject("head").get("cmd").toString();
                } catch (Exception e) {
                    log.info("[参数有问题]");
                    cmd = reqBody.getJSONObject("head").get("cmd").toString();
                    e.printStackTrace();
                    log.info("[发生系统错误，错误原因={}]", "请求报文cmd异常格式不正确");
                    ERROR(rep, new PlatException("创建发生错误，请求报文发生错误！！！"), cmd);
                    return;
                }
                String bean;
                String methodName;
                Method method;
                Method[] methods;
                try {
                    bean = cmd.split("\\.")[0];
                    methodName = cmd.split("\\.")[1];
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("[发生系统错误，错误原因={}]", "请求报文cmd异常格式不正确");
                    ERROR(rep, new PlatException("创建发生错误"), cmd);
                    return;
                }
                try {
                    methods = ReflectClassUtil.getMethod(App.getBean(bean).getClass());
                    method = matchMethod(methods, methodName);
                    result = ReflectionUtils.invokeMethod(method, App.getBean(bean), reqBody);//拆分命令执行目标方法
                } catch (PlatException platException) {
                    ERROR(rep, platException, cmd);
                    return;
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("[发生系统错误，错误原因={}]", e.getMessage());
                    ERROR(rep, new PlatException("系统发生错误，请联系管理"), cmd);
                    return;
                }
            } else {
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
                Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
                log.info("上传文件");
                //这个部分走上传的service 处理上传文件
                Object s = upload(fileMap);
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("url", s);
                result = jsonObject;
                jsonObject.put("name", "上传文件");
            }
            resp(rep, result, cmd);
            return;
        }
        if (req.getMethod().equals("GET")) {
            ResultEntity resultEntity;
            JSONObject reqBody;
            try {
                reqBody = getReqBody(req);
            } catch (Exception e) {
                ERROR(rep, new PlatException("报文格式错误"), "报文格式不正确");
                return;
            }
            try {
                checkReqBody(reqBody);
            } catch (Exception e) {
                ERROR(rep, new PlatException("报文错误"), "报文格式不正确");
                return;
            }
            BeanAndMethod beanAndMethod;
            try {
                beanAndMethod = getBeanAndMethod(reqBody);
            } catch (Exception e) {
                ERROR(rep, new PlatException("请检查命令，命令出错，该命令不存在"), reqBody.getJSONObject("head").getString("cmd"));
                return;
            }
            try {
                resultEntity = getResult(req, reqBody, beanAndMethod);
            } catch (PlatException platException) {
                ERROR(rep, platException, reqBody.getJSONObject("head").getString("cmd"));
                return;
            }
            resp(rep, resultEntity.getResult(), reqBody.getJSONObject("head").getString("cmd"));
        }
        if (req.getMethod().equals("PUT")) {
            log.info("暂不支持PUT方式");
            ERROR(rep, new PlatException("在暂不支持PUT"), "在暂不支持PUT");
            return;
        }
        if (req.getMethod().equals("DELETE")) {
            log.info("暂不支持DELETE方式");
            ERROR(rep, new PlatException("在暂不支持DELETE"), "在暂不支持DELETE");
            return;
        }

    }

    private ResultEntity getResult(HttpServletRequest req, JSONObject reqBody, BeanAndMethod beanAndMethod) throws PlatException {
        Object result;

        try {
            Method[] methods = ReflectClassUtil.getMethod(App.getBean(beanAndMethod.getBeanName()).getClass());
            Method method = matchMethod(methods, beanAndMethod.getMethodName());
            result = ReflectionUtils.invokeMethod(method, App.getBean(beanAndMethod.getBeanName()), reqBody);//拆分命令执行目标方法
        } catch (PlatException platException) {
            throw platException;
        } catch (Exception e) {
            e.printStackTrace();
            log.info("[发生系统错误，错误原因={}]", e.getMessage());
            throw new PlatException("系统发生错误，请联系管理");
        }
        ResultEntity resultEntity = new ResultEntity();
        resultEntity.setResult(result);
        resultEntity.setParam(reqBody);
        return resultEntity;
    }

    private BeanAndMethod getBeanAndMethod(JSONObject reqBody) throws Exception {
        BeanAndMethod beanAndMethod = new BeanAndMethod();
        String cmd = reqBody.getJSONObject("head").get("cmd").toString();
        String bean = cmd.split("\\.")[0];
        String methodName = cmd.split("\\.")[1];
        beanAndMethod.setBeanName(bean);
        beanAndMethod.setMethodName(methodName);
        beanAndMethod.setCmd(cmd);
        return beanAndMethod;
    }

    /**
     * 获取http请求中参数
     */
    private JSONObject getReqBody(HttpServletRequest req) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append(req.getReader().readLine());
        JSONObject reqBody = JSONObject.parseObject(sb.toString().trim());
        checkReqBody(reqBody);
        return reqBody;
    }

    /**
     * 检查请求报文
     */
    private void checkReqBody(JSONObject reqBody) throws Exception {
        if (!reqBody.containsKey("head")) {
            throw new Exception("请求报文有问题，请检查请求报文！！！");
        }
        if (!reqBody.containsKey("body")) {
            //此处不做报文结构解释，只在文档中体现，防止恶意攻击
            throw new Exception("请求报文有问题，请检查请求报文！！！");
        }
        if (!reqBody.getJSONObject("head").containsKey("cmd")) {
            throw new Exception("请求报文有问题，请检查请求报文！！！");
        }

    }

    private Object upload(Map<String, MultipartFile> fileMap) throws IOException, WxErrorException {
        String uploadPath = systemParam.getParam("sys:upload:url");
        String url = systemParam.getParam("sys:admin:video:url");
        List<String> list = new ArrayList<>();
        String filePath = "";
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            String path = FileUploadUtils.upload(uploadPath, entity.getValue());
            String compressPath = uploadPath + path;
            Thumbnails.of(compressPath)
                    .scale(1f)
                    .outputQuality(0.2f)
                    .toFile(compressPath);
            // list.add(path);
            // QiNiuUtil.upload(compressPath,entity.getValue().getOriginalFilename(),true);
            String key = entity.getValue().getOriginalFilename();
            QiNiuUtil.uploadMultipartFile(FileUtils.get(compressPath), key, true);
            String fileUrl = PropertiesUtil.getQiniuFileUrl() + "/" + key;
            list.add(fileUrl);
            filePath = compressPath;
        }
        List<String> urlList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            // urlList.add(url + list.get(i));//存放本地url
            urlList.add(list.get(i));
        }
        if (fileMap.containsKey("wxliveFile")) {
            //获取mediaUrl
            MultipartFile file = fileMap.get("wxliveFile");
            System.out.printf(file.getName());
            WxMediaUploadResult wxMediaUploadResult = wxMaService.getMediaService().uploadMedia("image", new File(filePath));
            System.out.printf("sasasa");
            wxMediaUploadResult.getMediaId();
            urlList.add(wxMediaUploadResult.getMediaId());
        }
        log.info("上传地址" + urlList);
        return urlList;
    }

    private Method matchMethod(Method[] meth, String methodName) {
        Method me = null;
        for (Method method : meth) {
            if (method.getName().equals(methodName)) {
                me = method;
                break;
            }
        }
        if (null == me || null == meth) {
            throw new PlatException(RespCode.NOT_FOUND_METHOD.getCode(), RespCode.NOT_FOUND_METHOD.getError_msg(), "没有找到访问的方法");
        }
        return me;
    }

    private void resp(HttpServletResponse rep, Object object, String cmd) {
        OutputStream out = null;
        try {
            rep.setHeader("Content-type", "application/json");
            JSONObject json = new JSONObject(true);
            json.put("code", RespCode.SUCCESS.getCode());
            json.put("success", RespCode.SUCCESS.getError_msg());
            json.put("timestamp", new Date());
            json.put("msg", RespCode.SUCCESS.getError_msg());
            json.put("cmd", cmd);
            json.put("body", object);
            log.info("响应报文：" + json.toString());
            out = rep.getOutputStream();
            out.write(json.toString().getBytes("UTF-8"));
            out.flush();
        } catch (Exception e) {
            log.info("返回报文时异常");
        } finally {
            try {
                out.close();
            } catch (Exception e) {
                log.info("关闭返回报文异常");
            }
        }
    }

    //    {
//          "code":"2000",
//            "success":"success",
//            "timestimp":"202001122",
//            "msg":"成功",
//            "cmd":"cmd",
//            "service":"user-center",
//            "body":{
//
//    }
//    }
    //构建错误的的报文响应
    private void ERROR(HttpServletResponse rep, PlatException platException, String cmd) {
        OutputStream out = null;
        String msg = platException.getMessage();
        String code = platException.getCode();
        String desc = platException.getDesc();

        try {
            rep.setHeader("Content-type", "application/json");
            JSONObject json = new JSONObject(true);
            JSONObject body = new JSONObject(true);
            body.put("msg", msg);
            json.put("code", code);
            json.put("success", RespCode.FAIL.getError_msg());
            json.put("timestamp", new Date());
            json.put("msg", RespCode.FAIL.getError_msg());
            json.put("desc", desc);
            json.put("cmd", cmd);
            json.put("body", body);
            log.info("错误响应报文：" + json.toString());
            out = rep.getOutputStream();
            out.write(json.toString().getBytes("UTF-8"));
            out.flush();
        } catch (Exception e) {
            log.info("返回报文时异常");
        } finally {
            try {
                out.close();
            } catch (Exception e) {
                log.info("关闭返回报文异常");
            }
        }
    }

    @ApiOperation(value = "支付回调通知处理")
    @PostMapping(value = "/api/notifyWeiXinPay")
    public String notifyWeiXinPay(@RequestBody String xmlData) throws WxPayException {
        final WxPayOrderNotifyResult notifyResult = wxService.parseOrderNotifyResult(xmlData);
        log.info("[支付回调，回调时间={},支付回调报文={}]", new Date(), JSONObject.toJSON(notifyResult));
        // TODO 根据自己业务场景需要构造返回对象
        PlatOrderEntity platOrderEntity = platOrderDao.getSQLManager().lambdaQuery(PlatOrderEntity.class).andEq("order_no", notifyResult.getOutTradeNo()).single();
        if (platOrderEntity == null) {
            PlatAuctionOrderRecordEntity record = platAuctionOrderRecordDao.getSQLManager().lambdaQuery(PlatAuctionOrderRecordEntity.class).andEq("auction_main_order", notifyResult.getOutTradeNo()).single();
            PlatOrderDetailEntity platOrderDetailEntity = platOrderDetailDao.getSQLManager().lambdaQuery(PlatOrderDetailEntity.class).andEq("goods_id", record.getGoodsId()).andEq("user_id", record.getUserId()).single();
            platOrderEntity = platOrderDao.getSQLManager().lambdaQuery(PlatOrderEntity.class).andEq("order_no", platOrderDetailEntity.getOrderNo()).single();
        }
        platOrderEntity.setOrderSn(notifyResult.getTransactionId());
        platOrderEntity.setPayStatus(OrderType.FINISH_PAY);
        platOrderEntity.setOrderSn(notifyResult.getTransactionId());
        platOrderEntity.setPayTime(new Date());
        sale(notifyResult, platOrderEntity);

        return WxPayNotifyResponse.success("成功");
    }


    private void sale(WxPayOrderNotifyResult notifyResult, PlatOrderEntity platOrderEntity) {
        if (redisCache.hasKey(notifyResult.getOutTradeNo())) {
            redisCache.deleteObject(notifyResult.getOutTradeNo());
            Date payTime = new Date();
            platOrderEntity.setPayTime(payTime);
            if (platOrderEntity.getOrderType().equals(OrderType.AUCTION)) {
                PlatAuctionOrderRecordEntity entity = platAuctionOrderRecordDao.getSQLManager().lambdaQuery(PlatAuctionOrderRecordEntity.class).andEq("out_trade_no", notifyResult.getOutTradeNo()).single();
                Double a = Double.valueOf(notifyResult.getTotalFee()) / 100;
                platOrderEntity.setLastAddPrice(new BigDecimal(a));
                entity.setPayTime(payTime);
                entity.setOutTradeNo(notifyResult.getOutTradeNo());
                entity.setThisAmount(a);
                entity.setPaySn(notifyResult.getTransactionId());
                entity.setPayStatus(OrderType.FINISH_PAY);
                platAuctionOrderRecordDao.updateById(entity);
            }
            platOrderEntity.setOrderSn(notifyResult.getTransactionId());
            platOrderEntity.setPayTime(new Date());
            platOrderDao.updateById(platOrderEntity);
        }
    }

    @ApiOperation(value = "退款回调通知处理")
    @PostMapping(value = "/api/notifyWeiXinRefund")
    public String notifyWeiXinRefund(@RequestBody String xmlData) throws WxPayException {
        final WxPayRefundNotifyResult result = this.wxService.parseRefundNotifyResult(xmlData);
        // TODO 根据自己业务场景需要构造返回对象

        return WxPayNotifyResponse.success("成功");
    }

    @ApiOperation(value = "拍卖付款回调通知")
    @PostMapping(value = "/api/notifyWeiXinAuction")
    public String notifyWeiXinAuction(@RequestBody String xmlData) throws WxPayException {
        final WxPayOrderNotifyResult result = this.wxService.parseOrderNotifyResult(xmlData);
        // TODO 根据自己业务场景需要构造返回对象

        return WxPayNotifyResponse.success("成功");
    }

    public static void main(String[] args) {
        String cmd = "phoneService.getName";
        String[] a = cmd.split("\\.");
        System.out.println(a);
    }
}
